//------------------------------------------------------------------------------
//
// Generic Brick Service Types
//
// Written Jan-2008 by Trevor Taylor, Software Technology, Australia
//
// IMPORTANT NOTE:
// This service does NOT have to replace existing services. It can co-exist
// with the AnalogSensor, Battery, ContactSensor, and SonarSensor services.
// You can chose to implement replacements for these services here to cut
// down on the overhead (by combining services) or continue to use them
// independently. Even if these services still have to call the Brick, this
// can be done via the Brick's private operations, not the generic ones.
// 
// A HardwareIdentifier is used in some of the other generic contracts.
// Be aware however that these Ids are not globally unique -- they are
// only meaningful within a particular Brick service.
//
// The Port and Pin number refer to physical pins on the PIC or MCU chip.
// These are not necessarily unique because some PICs have separate input
// and output ports and use the same numbering for both.
//
// Many PICs have PortA, PortB, PortC, etc. There is no standard for how
// many pins there are in each port, e.g. 8, 16, etc. or how these ports
// are labelled.
// To eliminate all of this, the HardwareIdentifiers might be numbered
// sequentially in logical order.
// For example:
// PortA - 8 bits
// PortB - 16 bits
// PortC - 8 bits
// Numbering is therefore 0-7, 8-23, 24-31
// which is a total of 32 pins as expected.
//
// However, devices such as the PICAXE break this numbering scheme
// by having separate Input and Output pins with the same numbers.
// In that case, just add a constant to one set of numbers to keep
// them all unique.
//
// You can also add "pseudo pins". For example, a "pin" might be
// defined that uses the value of an IR sensor to decide if there
// is an obstacle in the robot's path, i.e. a virtual bumper.
// This device can be exposed using both the raw IR value (which
// is analog) and a digital version (on or off).
//
//------------------------------------------------------------------------------

using Microsoft.Ccr.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using System;
using System.Collections.Generic;
using W3C.Soap;
using genericbrick = ProMRDS.Robotics.GenericBrick;

// This is required for some of the attributes
using System.ComponentModel;
// For HTTP operations
using Microsoft.Dss.Core.DsspHttp;

using drive = Microsoft.Robotics.Services.Drive.Proxy;


namespace ProMRDS.Robotics.GenericBrick
{
    /// <summary>
    /// GenericBrick Contract class
    /// </summary>
    [DisplayName("Generic Brick Service")]
    [Description("Generic contract for a simple robot Brick (the onboard brain)")]
    public sealed class Contract
    {
        /// <summary>
        /// The Dss Service contract
        /// </summary>
        public const String Identifier = "http://www.promrds.com/contracts/2008/01/genericbrick.html";
    }
    
    /// <summary>
    /// GenericBrick Main Operations Port
    /// </summary>
    /// <remarks>Keep the number of operations to 20 or less</remarks>
    [ServicePort()]
    public class GenericBrickOperations : PortSet
    {
        public GenericBrickOperations() : base(
            typeof(DsspDefaultLookup),
            typeof(DsspDefaultDrop),
            typeof(Get),
            typeof(HttpGet),
            typeof(HttpPost),
            typeof(Replace),            // Replaces entire state
            typeof(Subscribe),          // Subscribe to changes
            typeof(ConfigureBrick),     // Changes Brick Configuration
            typeof(ConfigureDevices),   // Changes Devices (if possible)
            typeof(UpdateSensors),      // Selective update / notification
            typeof(PlayTone),           // Play a Tone
            typeof(SetLEDs),            // Turn LEDs on/off
            typeof(GetSwitches),        // Read all switches
            typeof(GetSensors),         // Get list of Digital and Analog Inputs
            typeof(SetActuators),       // Set list of Digital and Analog Outputs
            typeof(QueryDrivePower),    // Query current Drive Power
            typeof(drive.SetDrivePower),// Update power to motors
            typeof(drive.DriveDistance),// Drive a specified distance
            typeof(drive.RotateDegrees) // Rotate a specified number of degrees
        )
        {
        }

        #region Implicit Operators
        /*

        /// <summary>
        /// Implicit Operator for Port of DsspDefaultLookup
        /// </summary>
        /// <param name="portSet"></param>
        /// <returns></returns>
        public static implicit operator Port<DsspDefaultLookup>(GenericBrickOperations portSet)
        {
            if (portSet == null) return null;
            return (Port<DsspDefaultLookup>)portSet[typeof(DsspDefaultLookup)];
        }
        /// <summary>
        /// Implicit Operator for Port of DsspDefaultDrop
        /// </summary>
        /// <param name="portSet"></param>
        /// <returns></returns>
        public static implicit operator Port<DsspDefaultDrop>(GenericBrickOperations portSet)
        {
            if (portSet == null) return null;
            return (Port<DsspDefaultDrop>)portSet[typeof(DsspDefaultDrop)];
        }
        /// <summary>
        /// Implicit Operator for Port of Get
        /// </summary>
        /// <param name="portSet"></param>
        /// <returns></returns>
        public static implicit operator Port<Get>(GenericBrickOperations portSet)
        {
            if (portSet == null) return null;
            return (Port<Get>)portSet[typeof(Get)];
        }
        /// <summary>
        /// Implicit Operator for Port of HttpGet
        /// </summary>
        /// <param name="portSet"></param>
        /// <returns></returns>
        public static implicit operator Port<HttpGet>(GenericBrickOperations portSet)
        {
            if (portSet == null) return null;
            return (Port<HttpGet>)portSet[typeof(HttpGet)];
        }
        /// <summary>
        /// Implicit Operator for Port of HttpPost
        /// </summary>
        /// <param name="portSet"></param>
        /// <returns></returns>
        public static implicit operator Port<HttpPost>(GenericBrickOperations portSet)
        {
            if (portSet == null) return null;
            return (Port<HttpPost>)portSet[typeof(HttpPost)];
        }
        /// <summary>
        /// Implicit Operator for Port of Subscribe
        /// </summary>
        /// <param name="portSet"></param>
        /// <returns></returns>
        public static implicit operator Port<Subscribe>(GenericBrickOperations portSet)
        {
            if (portSet == null) return null;
            return (Port<Subscribe>)portSet[typeof(Subscribe)];
        }
        */
        #endregion

        #region Post Overrides
        /// <summary>
        /// Post(DsspDefaultLookup)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(DsspDefaultLookup item) { base.PostUnknownType(item); }

        /// <summary>
        /// Post(DsspDefaultDrop)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(DsspDefaultDrop item) { base.PostUnknownType(item); }

        /// <summary>
        /// Post(Get)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(Get item) { base.PostUnknownType(item); }

        /// <summary>
        /// Post(HttpGet)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(HttpGet item) { base.PostUnknownType(item); }

        /// <summary>
        /// Post(HttpPost)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(HttpPost item) { base.PostUnknownType(item); }

        /// <summary>
        /// Post(Subscribe)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(Subscribe item) { base.PostUnknownType(item); }

        /// <summary>
        /// Post(PlayTone)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(PlayTone item) { base.PostUnknownType(item); }

        /// <summary>
        /// Post(SetDrivePower)
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public void Post(drive.SetDrivePower item) { base.PostUnknownType(item); }

        #endregion

        #region Helper Methods
        /*
        /// <summary>
        /// Post Subscribe and return the response port.
        /// </summary>
        public virtual PortSet<SubscribeResponseType, Fault> Subscribe(IPort notificationPort)
        {
            Subscribe op = new Subscribe();
            op.Body = new SubscribeRequestType();
            op.NotificationPort = notificationPort;
            this.Post(op);
            return op.ResponsePort;

        }

        /// <summary>
        /// Post Subscribe with body and return the response port.
        /// </summary>
        public virtual PortSet<SubscribeResponseType, Fault> Subscribe(SubscribeRequestType body, IPort notificationPort)
        {
            Subscribe op = new Subscribe();
            op.Body = body ?? new SubscribeRequestType();
            op.NotificationPort = notificationPort;
            this.Post(op);
            return op.ResponsePort;

        }
        */
        /// <summary>
        /// Post SetDrivePower with power settings and return the response port
        /// </summary>
        public virtual PortSet<DefaultUpdateResponseType, Fault> SetDrivePower(double left, double right)
        {
            drive.SetDrivePower op = new drive.SetDrivePower();
            op.Body.LeftWheelPower = left;
            op.Body.RightWheelPower = right;
            this.Post(op);
            return op.ResponsePort;

        }
        #endregion

    }

    #region Operations

    // Get -------------------------------------------

    /// <summary>
    /// Generic Brick Get Operation
    /// </summary>
    public class Get : Get<GetRequestType, PortSet<GenericBrickState, Fault>>
    {
        
        /// <summary>
        /// Generic Brick Get Operation
        /// </summary>
        public Get()
        {
        }
        
        /// <summary>
        /// Generic Brick Get Operation
        /// </summary>
        public Get(Microsoft.Dss.ServiceModel.Dssp.GetRequestType body) : 
                base(body)
        {
        }
        
        /// <summary>
        /// Generic Brick Get Operation
        /// </summary>
        public Get(Microsoft.Dss.ServiceModel.Dssp.GetRequestType body, Microsoft.Ccr.Core.PortSet<GenericBrickState,W3C.Soap.Fault> responsePort) : 
                base(body, responsePort)
        {
        }
    }

    // Replace -------------------------------------------

    /// <summary>
    /// Generic Brick Replace Operation
    /// </summary>
    [DisplayName("Replace")]
    [Description("Changes (or indicates a change to) the Brick's entire state")]
    public class Replace : Replace<GenericBrickState, PortSet<DefaultReplaceResponseType, Fault>>
    {
    }

    // Subscribe -------------------------------------------

    /// <summary>
    /// Generic Brick Subscribe to Notifications Operation
    /// </summary>
    [Description("Subscribe to Brick notifications")]
    public class Subscribe : Subscribe<SubscribeRequestType, PortSet<SubscribeResponseType, Fault>>
    {
    }

    // ConfigureBrick -------------------------------------------

    /// <summary>
    /// Generic Brick Configure Brick Operation
    /// </summary>
    /// <remarks>Might not be allowed to update all properties</remarks>
    [DisplayName("Configure Brick")]
    [Description("Reconfigures the Brick")]
    public class ConfigureBrick : Update<CommsConfig, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    // ConfigureDevices -------------------------------------------

    /// <summary>
    /// Generic Brick Configure Devices Operation
    /// </summary>
    [DisplayName("Configure Devices")]
    [Description("Reconfigures Devices (if possible)")]
    public class ConfigureDevices : Update<ConfigureDevicesRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    /// <summary>
    /// Configure Devices Request
    /// </summary>
    [DataContract, Description("Reconfigure Devices on the Brick")]
    [DataMemberConstructor]
    public class ConfigureDevicesRequest
    {
        /// <summary>
        /// List of Devices to Reconfigure
        /// </summary>
        [DataMember, Description("Devices as a List")]
        [DataMemberConstructor]
        public List<Device> Devices;

    }

    // UpdateSensors -------------------------------------------

    /// <summary>
    /// Generic Brick Update Sensors Operation (and Notifications)
    /// </summary>
    [DisplayName("Update Sensors")]
    [Description("Changes one or more of the Sensor values (Also used for Notifications)")]
    public class UpdateSensors : Update<UpdateSensorsRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
        public UpdateSensors()
        {
            if (this.Body == null)
                this.Body = new UpdateSensorsRequest();

            this.Body.Devices = new List<Device>();
        }
        public UpdateSensors(List<Device> sensors)
        {
            if (this.Body == null)
                this.Body = new UpdateSensorsRequest();

            this.Body.Devices = sensors;
        }
    }

    /// <summary>
    /// Update Sensors Request
    /// </summary>
    [DataContract, Description("Update Sensor values on the Brick")]
    [DataMemberConstructor]
    public class UpdateSensorsRequest
    {
        /// <summary>
        /// List of Devices to Update
        /// </summary>
        [DataMember, Description("Devices as a List")]
        [DataMemberConstructor]
        public List<Device> Devices;

    }

    // PlayTone -------------------------------------------

    /// <summary>
    /// Generic Brick Play Tone Operation
    /// </summary>
    [Description("Plays a tone for a specified amount of time")]
    public class PlayTone : Update<PlayToneRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
        public PlayTone()
        {
        }

        public PlayTone(int frequency, int duration)
        {
            this.Body.Frequency = frequency;
            this.Body.Duration = duration;
        }
    }

    /// <summary>
    /// Play Tone Request
    /// </summary>
    /// <remarks>Frequency and/or Duration might be ignored, i.e. simple one-shot beeper</remarks>
    [DataContract]
    [DataMemberConstructor]
    public class PlayToneRequest
    {
        private int _frequency;
        private int _duration;

        /// <summary>
        /// Frequency of the tone to play (Hertz)
        /// </summary>
        [DataMember]
        [DataMemberConstructor]
        public int Frequency
        {
            get { return _frequency; }
            set { _frequency = value; }
        }

        /// <summary>
        /// How long to play the tone (milliseconds)
        /// </summary>
        [DataMember]
        [DataMemberConstructor]
        public int Duration
        {
            get { return _duration; }
            set { _duration = value; }
        }
    }

    // SetLEDs -------------------------------------------

    /// <summary>
    /// Generic Brick Set LEDs Operation
    /// </summary>
    [Description("Sets the LEDs on a Brick all at once")]
    public class SetLEDs : Update<SetLEDsRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
        /// <summary>
        /// Set LEDs on the Brick
        /// </summary>
        public SetLEDs()
        {
        }
        /// <summary>
        /// Set LEDs on the Brick
        /// </summary>
        public SetLEDs(SetLEDsRequest body)
            : base(body)
        {
        }

        /// <summary>
        /// Set LEDs on the Brick all at once
        /// </summary>
        public SetLEDs(int LEDs)
        {
            this.Body.States = LEDs;
        }
    }

    /// <summary>
    /// Set LEDs Request
    /// </summary>
    [DataContract, Description("Set the LEDs on the Brick")]
    [DataMemberConstructor]
    public class SetLEDsRequest
    {
        /// <summary>
        /// LED states as a binary bitmask
        /// </summary>
        [DataMember, Description("LED states as a binary bitmask")]
        [DataMemberConstructor]
        public int States;

        public SetLEDsRequest()
        {
        }
    }

    // GetSwitches -------------------------------------------

    /// <summary>
    /// Generic Brick Get Switches
    /// </summary>
    [Description("Gets the Switches on a Brick all at once")]
    public class GetSwitches : Query<GetSwitchesRequest, PortSet<GetSwitchesResponse, Fault>>
    {
        /// <summary>
        /// Get the Switches on the Brick
        /// </summary>
        public GetSwitches()
        {
        }
        /// <summary>
        /// Get the Switches on the Brick
        /// </summary>
        public GetSwitches(GetSwitchesRequest body)
            : base(body)
        {
        }
        /// <summary>
        /// Get the Switches on the Brick
        /// </summary>
        public GetSwitches(GetSwitchesRequest body, PortSet<GetSwitchesResponse, Fault> responsePort)
            : base(body, responsePort)
        {
        }
    }

    /// <summary>
    /// Get Switches Request
    /// </summary>
    [DataContract, Description("Get the Switches on the Brick")]
    public class GetSwitchesRequest
    {
    }

    /// <summary>
    /// Get Switches Response
    /// </summary>
    [DataContract, Description("Returns the Switches on the Brick")]
    public class GetSwitchesResponse
    {
        /// <summary>
        /// Switch states as a binary bitmask
        /// </summary>
        [DataMember, Description("Switch states as a binary bitmask")]
        [DataMemberConstructor]
        public int States;

        public GetSwitchesResponse()
        {
        }
    }

    // GetSensors -------------------------------------------

    /// <summary>
    /// Generic Brick Get Sensors (Digital/Analog Inputs) Operation
    /// </summary>
    [Description("Gets specified Sensors (Digital/Analog Inputs) on a Brick")]
    public class GetSensors : Query<GetSensorsRequest, PortSet<GetSensorsResponse, Fault>>
    {
        /// <summary>
        /// Get the Inputs on the Brick
        /// </summary>
        public GetSensors()
        {
        }
        /// <summary>
        /// Get the Inputs on the Brick
        /// </summary>
        public GetSensors(GetSensorsRequest body)
            : base(body)
        {
        }
        /// <summary>
        /// Get the Inputs on the Brick
        /// </summary>
        public GetSensors(GetSensorsRequest body, PortSet<GetSensorsResponse, Fault> responsePort)
            : base(body, responsePort)
        {
        }
    }

    /// <summary>
    /// Get Sensors (Digital/Analog Inputs) Request
    /// </summary>
    [DataContract, Description("Get Sensors (Digital/Analog Inputs) on the Brick")]
    public class GetSensorsRequest
    {
        /// <summary>
        /// Digital Inputs as a List
        /// </summary>
        [DataMember, Description("Inputs as a Device List (Empty = Get All)")]
        [DataMemberConstructor]
        public List<Device> Inputs;
    }

    /// <summary>
    /// Get Sensors (Digital/Analog Inputs) Response
    /// </summary>
    [DataContract, Description("Returns Sensor values (Digital/Analog Inputs) on the Brick")]
    public class GetSensorsResponse
    {
        /// <summary>
        /// Digital Inputs as a List
        /// </summary>
        [DataMember, Description("Inputs as a Device List")]
        [DataMemberConstructor]
        public List<Device> Inputs;
        /*
        public GetSensorsResponse()
        {
        }
         */
    }

    // SetActuators -------------------------------------------

    /// <summary>
    /// Generic Brick Set Actuators (Digital/Analog Outputs) Operation
    /// </summary>
    [Description("Sets Actuators (Digital/Analog Outputs) on a Brick")]
    public class SetActuators : Update<SetActuatorsRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
        /// <summary>
        /// Set Actuators on the Brick
        /// </summary>
        public SetActuators()
        {
        }
        /// <summary>
        /// Set Actuators on the Brick
        /// </summary>
        public SetActuators(SetActuatorsRequest body)
            : base(body)
        {
        }
        /// <summary>
        /// Set Actuators on the Brick
        /// </summary>
        public SetActuators(SetActuatorsRequest body, PortSet<DefaultUpdateResponseType, Fault> responsePort)
            : base(body, responsePort)
        {
        }
    }

    /// <summary>
    /// Set Actuators (Digital/Analog Outputs) Request
    /// </summary>
    [DataContract, Description("Sets specified Actuators (Digital/Analog Outputs) on the Brick")]
    public class SetActuatorsRequest
    {
        /// <summary>
        /// Actuators as a List
        /// </summary>
        [DataMember, Description("Outputs as a Device List")]
        [DataMemberConstructor]
        public List<Device> Outputs;

    }

    // QueryDrivePower -------------------------------------------

    [DisplayName("Query Drive Power")]
    [Description("Gets the Drive power (for both motors)")]
    public class QueryDrivePower : Query<QueryDrivePowerRequest, PortSet<DrivePower, Fault>>
    {
    }

    [Description("Query Drive Power Request")]
    [DataContract]
    public class QueryDrivePowerRequest
    {
    }

    /*
    // UpdateDrivePower -------------------------------------------

    [DisplayName("Update Drive Power")]
    [Description("Updates (or indicates an update to) Drive power")]
    public class UpdateDrivePower : Update<DrivePower, PortSet<DefaultUpdateResponseType, Fault>>
    {
        public UpdateDrivePower()
        {
            if (this.Body == null)
                this.Body = new DrivePower();
            this.Body.LeftPower = 0;
            this.Body.RightPower = 0;
        }
        public UpdateDrivePower(DrivePower drivePower)
        {
            this.Body = drivePower;
        }
        public UpdateDrivePower(double leftPower, double rightPower)
        {
            this.Body.LeftPower = leftPower;
            this.Body.RightPower = rightPower;
        }
    }
    */
    #endregion

    #region Enums

    /// <summary>
    /// Brick Capabilities
    /// </summary>
    /// This is intended to allow an "intelligent" UI to figure out what the
    /// brick can do and display appropriate controls.
    /// Smart services can also decide whether or not to make certain requests.
    /// Some of this information is redundant because you can look at the device
    /// list and figure it out. However, this brings it all together.
    /// 
    /// The flags with a prefix of "Has" can be determined by scanning the
    /// device lists, but putting the flags into the BrickCapabilities makes
    /// it much quicker and easier to figure out.
    /// 
    /// The flags with a prefix of "Can" indicate what the brick can do,
    /// which determines which operations are implemented. Intelligent
    /// partners can avoid "Not Implemented" Faults by checking first.
    /// 
    [DataContract]
    [Flags]
    public enum BrickCapabilities : int
    {
        HasDigitalIn        = 0x0001,
        HasDigitalOut       = 0x0002,
        HasAnalogIn         = 0x0004,
        HasAnalogOut        = 0x0008,

        HasBuzzer           = 0x0010,
        HasLEDs             = 0x0020,
        HasSwitches         = 0x0040,
        HasBumpers          = 0x0080,
        HasLineFollowing    = 0x0100,
        HasDifferentialDrive = 0x0200,

        CanPlayTones        = 0x1000,
        CanControlMotors    = 0x2000,
        CanDriveDistance    = 0x4000,
        CanRotateDegrees    = 0x8000,

        CanReconfigure      = 0x800000
    }

    /// <summary>
    /// Device Types Bit Field Enum
    /// </summary>
    //
    // NOTES ON PIN CONFIGURATION FOR PICs:
    //
    // Most PICs allow reconfiguration of the pins and it is quite
    // common for pins to have three or four possible functions.
    // In this case, the pin should be marked as Reconfigurable and
    // all of the different types should be included using logical OR.
    //
    // Even if a pin can be reconfigured on the PIC, the service
    // might fix the operating mode of the pin. This is important to
    // ensure that peripherials on particular pins are not disrupted,
    // e.g. a buzzer is an Output device so setting the pin to Input
    // is pointless and might even be considered to be an error (and
    // generate a Fault). In this case, do NOT mark the pin as
    // reconfigurable -- just set its type to match its purpose.
    //
    // Reconfigurable pins might have to be explicitly set to the
    // correct mode by making a ConfigureDevices request. Only a
    // single mode must be selected in this case. NOTE: This request
    // is NOT currently implemented!
    //
    // Alternatively, the service might be smart enough to reconfigure
    // pins dynamically if necessary. For instance, if a SetOutputs
    // request is made on the pin and it is currently an input, then it
    // should be switched to output mode first (unless the PIC does
    // this automatically).
    //
    // The CommsIn and CommsOut types are for completeness. It is not
    // anticipated that partner services would have any use for these.
    //
    // A value of zero for any field based on these flags means that
    // the device type is undefined or it is uninitialized.
    //
    // Flags can be combined using logical OR, e.g.
    // flags = DeviceTypes.DigitalIn | DeviceTypes.Reserved;
    //
    // However, in most cases a single flag will be set. But to be
    // sure that you are checking correcly you must use bitwise AND
    // operations, e.g.
    // if ((flags & DeviceTypes.DigitalOut) != 0)
    // { ... }
    //
    [DataContract]
    [Flags]
    public enum DeviceTypes : short
    {
        Other               = 0x0001,   // None of the ones below, or unspecified
        Reserved            = 0x0002,   // Reserved = DO NOT USE THIS DEVICE
        Reconfigurable      = 0x0004,   // Can be one of several types
        DigitalIn           = 0x0008,   // Only Digital Input
        DigitalOut          = 0x0010,   // Only Digital Output
        DigitalInOut        = 0x0020,   // Can be EITHER Input or Output (but no other type)
                                        // NOTE: This is NOT a logical OR of Digital In/Out
        AnalogIn            = 0x0040,   // ADC
        AnalogOut           = 0x0080,   // DAC
        CommsIn             = 0x0100,   // RS-232 Rx Data, or Rx Data for Bluetooth, etc.
        CommsOut            = 0x0200    // RS-232 Tx Data, or Tx Data for Bluetooth, etc.
    }

    /// <summary>
    /// Device Functions Enum
    /// </summary>
    // NOTES:
    // This list could go on forever! The intention is NOT to provide an
    // exhaustive list, however, there are plenty of numbers left.
    // Several items are aliased for convenience because they are often known
    // by different names.
    [DataContract]
    public enum DeviceFunctions : byte
    {
        Unknown             = 0,        // Should NOT be used -- indicates uninitialized state
        Reserved            = 1,        // Device should NOT be accessed (hard-wired or internal function)
                                        // Should also be used if Device does not exist or is unavailable
        Unused              = 2,        // No assigned function -- probably reconfigurable
        Other               = 3,        // None of the defined values are applicable
        LED                 = 4,        // Light Emitting Diode (or lamp, headlight, etc.)
        Light               = 4,        // Alias for LED
        Switch              = 5,        // On/Off switch (toggle, slide, etc)
        Button              = 5,        // Pushbutton (alias for Switch)
        Bumper              = 6,        // Whisker, contact switch, digital InfraRed (on/off only)
        ContactSensor       = 6,        // Alias for Bumper
        LineFollower        = 7,        // Line Follower faces the ground (Analog or Digital)
        CliffDetector       = 7,        // Alias for Line Follower (similar function)
        IRRangeSensor       = 8,        // Analog value
        SonarRangeSensor    = 9,        // Analog value
        LaserRangeSensor    = 10,        // Analog value
        Buzzer              = 11,       // Buzzer or piezo speaker (only beeps)
        Sound               = 12,       // Sound output, e.g. tones or wave files
        Comms               = 13,       // Communications, e.g. RS-232, Bluetooth, etc.
        Compass             = 14,       // Digital Compass in Degrees (analog input)
        Temperature         = 15,       // Degrees C (analog input)
        Voltage             = 16,       // Volts (analog input)
        BatteryPower        = 17,       // Some indication of Battery health
        MotorDirection      = 18,       // Controls the direction that a motor turns
        MotorPower          = 19,       // Controls the speed of a motor
        WheelEncoder        = 20,       // An encoder measures wheel rotations
        ServoPosition       = 21,       // Position of a servo (usually an angle)
    }

    /// <summary>
    /// Location of the Device on the Robot
    /// </summary>
    /// NOTE: This is a very much simplified version of a Pose which handles
    /// 90% of the common locations. Yes, it is very limited, but so are most
    /// small hobby robots!
    /// If you are not familiar with a compass, the 16 principal directions are:
    ///        NW  NNW  N  NNE   NE
    ///        WNW      |       ENE
    ///         W      -O-       E
    ///        WSW      |       ESE
    ///        SW  SSW  S  SSE   SE
    ///
    /// This does not accomodate sonar sensors spaced at 30 degree intervals.
    /// 
    /// The location names are based on the periphery:
    /// Front, Side, Rear
    /// or the body of the robot:
    /// Center
    /// as well as Line Following (facing the down towards the ground):
    /// Ground
    [DataContract, Description("Location of the Device on the Robot")]
    public enum Location : byte
    {
        Unknown         = 0,
        Other           = 255,  // Does not fit any of the descriptions below
        // These locations are not on the periphery - somewhere in center of the robot
        CenterFarLeft   = 51,
        CenterLeft      = 52,
        Center          = 53,
        CenterRight     = 54,
        CenterFarRight  = 55,
        // Peripheral sensors around the robot --
        // "Compass directions" relative to the robot with North = Front
        // Aliases might be easier to use in many cases
        NW              = 2,    // North West
        FrontFarLeft    = 2,

        NNW             = 3,    // North North West
        FrontLeft       = 3,

        N               = 4,    // North
        FrontCenter     = 4,

        NNE             = 5,    // North North East
        FrontRight      = 5,

        NE              = 6,    // North East
        FrontFarRight   = 6,

        WNW             = 7,    // West North West
        FrontSideLeft   = 7,

        W               = 8,    // West
        SideLeft        = 8,

        WSW             = 9,    // West South West
        RearSideLeft    = 9,

        ENE             = 10,   // East North East
        FrontSideRight  = 10,

        E               = 11,   // East
        SideRight       = 11,

        ESE             = 12,   // East South East
        RearSideRight   = 12,

        SW              = 13,   // South West
        RearFarLeft     = 13,

        SSW             = 14,   // South South West
        RearLeft        = 14,

        S               = 15,   // South
        RearCenter      = 15,

        SSE             = 16,   // South South East
        RearRight       = 16,

        SE              = 17,   // South East
        RearFarRight    = 17,

        // Line Following and Cliff Detection
        GndNW               = 18,
        GroundFrontFarLeft  = 18,
        GndNNW              = 19,
        GroundFrontLeft     = 19,
        GndN                = 20,
        GroundFrontCenter   = 20,
        GndNNE              = 21,
        GroundFrontRight    = 21,
        GndNE               = 22,
        GroundFrontFarRight = 22,
        GndWNW              = 23,
        GroundFrontSideLeft = 23,
        GndW                = 24,
        GroundSideLeft      = 24,
        GndWSW              = 25,
        GroundRearSideLeft  = 25,
        GndENE              = 26,
        GroundFrontSideRight = 26,
        GndE                = 27,
        GroundSideRight     = 27,
        GndESE              = 28,
        GroundRearSideRight = 28,
        GndSW               = 29,
        GroundRearFarLeft   = 29,
        GndSSW              = 30,
        GroundRearLeft      = 30,
        GndS                = 31,
        GroundRearCenter    = 31,
        GndSSE              = 32,
        GroundRearRight     = 32,
        GndSE               = 33,
        GroundRearFarRight  = 33
    }

    #endregion

    #region Basic Hardware Types

    /// <summary>
    /// Class for all Devices (Analog/Digital, Input/Output)
    /// </summary>
    /// NOTE: A Device can be Analog or Digital. Therefore there are properties
    /// for both types of values. This is wasteful, but it makes it easier if the
    /// device can be reconfigured.
    [DataContract]
    [DataMemberConstructor]
    [Description("Basic details about a Device a.k.a. Pin")]
    public class Device
    {
        // Basic device information
        private string _name;
        private int _hardwareIdentifier;
        private DeviceTypes _type;
        private DeviceFunctions _function;
        private Location _location;

        // Additional fields to help manage the device
        // These should NOT be changed by external services!
        private int _port;
        private int _pin;

        // Current value properties
        private DateTime _timeStamp;
        private bool _state;
        private double _value;
        private double _minValue;
        private double _maxValue;

        /// <summary>
        /// Device Name
        /// </summary>
        [DataMember]
        [Description("User-friendly Name for the Device")]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Unique Hardware Identifier
        /// </summary>
        [DataMember]
        [Description("A unique, immutable indentifier for this particular device")]
        public int HardwareIdentifer
        {
            get { return _hardwareIdentifier; }
            set { _hardwareIdentifier = value; }
        }

        /// <summary>
        /// Device Type
        /// </summary>
        [DataMember]
        [Description("Type of the Device (See DeviceTypes enum)")]
        public DeviceTypes Type
        {
            get { return _type; }
            set { _type = (DeviceTypes)value; }
        }

        /// <summary>
        /// Device Function
        /// </summary>
        [DataMember]
        [Description("Function of the Device (See DeviceFunctions enum)")]
        public DeviceFunctions Function
        {
            get { return _function; }
            set { _function = (DeviceFunctions)value; }
        }

        /// <summary>
        /// Location of the Device on the Robot
        /// </summary>
        [DataMember]
        [Description("Location of the Device (See Location enum)")]
        public Location Location
        {
            get { return _location; }
            set { _location = (Location)value; }
        }

        /// <summary>
        /// Port on the PIC or MCU
        /// </summary>
        [DataMember]
        [Description("The physical Port the Device is attached to")]
        public int Port
        {
            get { return _port; }
            set { _port = value; }
        }

        /// <summary>
        /// Pin on the PIC or MCU
        /// </summary>
        [DataMember]
        [Description("The Pin the Device is attached to (within a Port)")]
        public int Pin
        {
            get { return _pin; }
            set { _pin = value; }
        }

        /// <summary>
        /// Time Stamp for last update
        /// </summary>
        [DataMember]
        [Description("Time Stamp for last update that took place")]
        public DateTime TimeStamp
        {
            get { return _timeStamp; }
            set { _timeStamp = value; }
        }

        /// <summary>
        /// Binary State of the Device
        /// </summary>
        [DataMember]
        [Description("Indicates if the Device state is currently high/low, on/off, etc.")]
        public bool State
        {
            get { return _state; }
            set { _state = value; }
        }

        /// <summary>
        /// Current Value
        /// </summary>
        [DataMember]
        [Description("Current Device value")]
        public double Value
        {
            get { return _value; }
            set { _value = value; }
        }

        /// <summary>
        /// Minimum Value
        /// </summary>
        [DataMember]
        [Description("Minimum Value")]
        public double MinValue
        {
            get { return _minValue; }
            set { _minValue = value; }
        }

        /// <summary>
        /// Maximum Value
        /// </summary>
        [DataMember]
        [Description("Maximum Value")]
        public double MaxValue
        {
            get { return _maxValue; }
            set { _maxValue = value; }
        }

    }

    #endregion

}
